package com.tbit.uqbike.tergateway.entity;

import com.alibaba.fastjson.JSON;
import com.tbit.uqbike.TerGatewayMain;
import com.tbit.uqbike.protocol.AnalyzeImpl.AAutoProtocol;
import com.tbit.uqbike.protocol.AnalyzeImpl.AProtocol;
import com.tbit.uqbike.service.redis.RedisService;
import com.tbit.uqbike.tergateway.config.TerGatewayConfig;
import com.tbit.uqbike.tergateway.data.TerGatewayData;
import com.tbit.uqbike.tergateway.log.LOG;
import com.tbit.uqbike.tergateway.pojo.TerAttrEx;
import com.tbit.uqbike.tergateway.pojo.TerBattery;
import com.tbit.uqbike.tergateway.pojo.TerInfo;
import com.tbit.uqbike.tergateway.pojo.TerOfflineOrder;
import com.tbit.uqbike.tergateway.pojo.TerPos;
import com.tbit.uqbike.util.StringUtil;
import com.tbit.uqbike.util.TerPubUtil;
import com.tbit.utils.BitUtil;
import io.netty.buffer.ByteBuf;

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by MyWin on 2017/5/10.
 * 终端内存数据
 */
public class TerTempData {
    public static final int DefaultSerNo = 0;
    public static final int MaxSerNo = 256;
    private static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(TerTempData.class);

    // 设备编号
    private String sn;
    // 连接id
    private String connId;
    private ReentrantLock _connIdLock;
    // 协议
    public String protocolName;

    // 特殊升级流程
    public int beforeOrderIndex = 0;
    public String beforeOrderSer = "";
    public int afterOrderIndex = 0;
    public String afterOrderStr = "";
    public String currDevVer = "";

    public static class FilterPosInfo{
        public double lon;
        public double lat;
        public String lacId;
        public String cellid;

        public FilterPosInfo(double lon, double lat, String lacId, String cellid) {
            this.lon = lon;
            this.lat = lat;
            this.lacId = lacId;
            this.cellid = cellid;
        }
    }

    public static class FilterRecord{
        // 连续过滤计数
        public int filterCnt = 0;
        public LinkedList<FilterPosInfo> pos = new LinkedList<>();

        public void addFilterPos(double lon, double lat, String lacId, String cellid) {
            pos.addLast(new FilterPosInfo(lon, lat, lacId, cellid));
            filterCnt++;
            if (pos.size() > 3) {
                pos.removeFirst();
            }
        }

        public double getAvgDis(double lon, double lat) {
            double totalDis = 0;
            for (FilterPosInfo fp : pos) {
                totalDis += (TerPubUtil.calculateDistance(fp.lon, fp.lat, lon, lat) * 1000);
            }
            return totalDis / pos.size();
        }

        public void clearEnvir() {
            filterCnt = 0;
            pos.clear();
        }
    }
    // 过滤相关的临时变量
    public FilterRecord fr = new FilterRecord();

    /**
     * 电量
     */
    private TerBattery lastBattery;
    /**
     * 位置
     */
    private TerPos lastPos;
    /**
     * 最后报文时间
     */
    private Date lastPkgDt;
    /**
     * 最后状态
     */
    private TerLastStatus status;
    /**
     * 协议相关
     */
    private ProtocolData protocolData;

    /**
     * 终端是否需要升级
     */
    private Boolean terSoftInfoFlag;
    /**
     * 标志是否有离线指令
     */
    private Boolean offlineOrderFlag;

    /**
     * 缓存对象，需要更新的对象都在这里
     * 暂时不加锁
     */
    private CacheData cacheData;

    /**
     *
     */
    private List<Map.Entry<Integer, Object>> _serList;
    private ReentrantLock _lock;

    public static final Integer SI_MIN_LEN = 5;
    public static final Integer SI_MAX_LEN = 10;
    public LinkedList<SignalInfo> siList = new LinkedList<>();


    /**
     * 更新连接id true表示改变了
     *
     * @param connId
     * @return
     */
    public boolean updateConnId(String connId) {
        try {
            _connIdLock.lock();
            if (Objects.equals(connId, this.connId)) {
                return false;
            } else {
                if (LOG.bConnMnoLog) {
                    LOG.NET.info(String.format("mno:%s,conn:%s", this.sn, connId));
                }
                this.connId = connId;
                return true;
            }
        } finally {
            _connIdLock.unlock();
        }
    }

    public String getConnId() {
        return this.connId;
    }

    public String getSn() {
        return sn;
    }

    public boolean haveCacheData() {
        return cacheData.haveDbCacheData() || cacheData.haveDbCacheData();
    }

    public void updateTerVerReve(byte version, int reserve) {
        boolean bupdate = false;
        if (null == protocolData) {
            protocolData = new ProtocolData(version, reserve);
            bupdate = true;
        } else {
            if (!protocolData.compare(version, reserve)) {
                protocolData.setVersion(version);
                protocolData.setReserve(reserve);
                bupdate = true;
            }
        }

        if (bupdate) {
            cacheData.setProtocolData(protocolData);
        }
    }

    public ProtocolData getProtocolData() {
        return protocolData;
    }

    public void updateRouteKey(String routeKey) {
        // 此处不需要比较，直接更新缓存
        // 由于有些场景需要立马获取routeKey调整成立马同步写入，不然会失败
        // TerGatewayData.terMqRouteKey, TerGatewayConfig.routeKey
        TerGatewayData.updateTerMqBinding(this.sn, TerGatewayConfig.routeKey);
        // 异步写入的暂时先不删除，反正对性能没啥影响
        TerGatewayData.putConnTidSet(this.sn);
    }

    public Date getLastPkgDt() {
        return lastPkgDt;
    }

    public void updateLastPkgDt(Date lastPkgDt) {
        long l = lastPkgDt.getTime();
        long old = 0;
        if (null != this.lastPkgDt) {
            old = this.lastPkgDt.getTime();
        }
        if (l != old) {
            this.lastPkgDt = new Date(lastPkgDt.getTime());
            cacheData.setLastPkgDt(this.lastPkgDt);
        }
    }

    public TerLastStatus getStatus() {
        return status;
    }

    public void updateStatus(Date dt, int carStatus, int signStatus) {
        TerLastStatus exist = this.status;
        if (exist == null || dt.getTime() > exist.statusDt.getTime()) {
            TerLastStatus terLastStatus = new TerLastStatus();
            terLastStatus.carStatus = carStatus;
            terLastStatus.statusDt = dt;
            terLastStatus.signStatus = signStatus;
            terLastStatus.mno = sn;

            // 做一下静止检测相关逻辑
            // 静止时间判定和处理
            TerAttrEx item = null;
            boolean stopFlag = !BitUtil.check(carStatus, 1);
            Boolean lastStopFlag = null;
            if (exist != null && exist.statusDt != null) {
                lastStopFlag = !BitUtil.check(exist.carStatus, 1);
            }

            if (lastStopFlag == null || lastStopFlag != stopFlag) {
                item = getTerStopAttr(sn, stopFlag);
            }

            if (null != item) {
                TerGatewayMain.getDbService().updateTerAttr(item);
            }

            if (null == exist || exist.carStatus != carStatus) {
                TerAttrEx statusItem = new TerAttrEx();
                statusItem.setMno(sn);
                statusItem.setAttrValue(Integer.toString(carStatus));
                statusItem.setAttrDt(dt);
                statusItem.setAttrName(TerGatewayData.terLastStat);
                TerGatewayMain.getDbService().updateTerAttr(statusItem);
            }

            if (null != item) {
                TerGatewayMain.getDbService().updateTerAttr(item);
            }

            this.status = terLastStatus;
            cacheData.setStatus(this.status);
        }
    }

    public static TerAttrEx getTerStopAttr(String mno, boolean bStop) {
        TerAttrEx item = new TerAttrEx();
        item.setMno(mno);
        if (bStop) {
            item.setAttrValue("0");
        } else {
            item.setAttrValue("1");
        }
        item.setAttrDt(new Date());
        item.setAttrName(TerGatewayData.terStopTime);
        return item;
    }

    /**
     * 更新电压
     *
     * @param dt
     * @param v
     */
    public void updateVol(Date dt, int v) {
        TerLastVol terLastVol = new TerLastVol();
        terLastVol.dt = dt;
        terLastVol.vol = v;
        terLastVol.mno = sn;

        cacheData.setVol(terLastVol);
    }

    public Boolean getOfflineOrderFlag() {
        return offlineOrderFlag;
    }

    public void setOfflineOrderFlag(Boolean offlineOrderFlag) {
        this.offlineOrderFlag = offlineOrderFlag;
    }

    public Boolean getTerSoftInfoFlag() {
        return terSoftInfoFlag;
    }

    public void setTerSoftInfoFlag(Boolean terSoftInfoFlag) {
        this.terSoftInfoFlag = terSoftInfoFlag;
    }

    /**
     * 从redis中一次性拉取所有的数据
     */
    public void refreshMemData() {
        RedisService redis = TerGatewayMain.getRedis();
        String hashId = TerGatewayData.getTerHashId(this.sn);
        Map<String, String> map = redis.getAll(hashId);
        refreshMemData(map);
    }

    public void refreshMemData(Map<String, String> map) {
        if (null != map) {
            Set<Map.Entry<String, String>> set = map.entrySet();
            for (Map.Entry<String, String> kv : set) {
                String key = kv.getKey();
                String value = kv.getValue();
                if (null == value || value.isEmpty()) {
                    continue;
                }
                if (Objects.equals(key, TerGatewayData.terLastPos)) {
                    try {
                        lastPos = JSON.parseObject(value, TerPos.class);
                        // 直接修正时间
                        if (null != lastPos && (lastPos.getDt().getTime() - System.currentTimeMillis()) > TerGatewayConfig.MaxHisPosSec * 1000) {
                            lastPos.setDt(new Date());
                        }
                    } catch (Exception e) {
                    }
                } else if (Objects.equals(key, TerGatewayData.terLastBattery)) {
                    try {
                        lastBattery = JSON.parseObject(value, TerBattery.class);
                    } catch (Exception e) {
                    }
                } else if (Objects.equals(key, TerGatewayData.terProtoclData)) {
                    try {
                        protocolData = JSON.parseObject(value, ProtocolData.class);
                    } catch (Exception e) {
                    }
                } else if(Objects.equals(key, TerGatewayData.terLastStatusKey)) {
                    try {
                        status = JSON.parseObject(value, TerLastStatus.class);
                    } catch (Exception e) {
                    }
                }
            }
        }
    }

    public TerTempData(String sn) {
        this.sn = sn;
        this._connIdLock = new ReentrantLock();
        this._lock = new ReentrantLock();
        this._serList = new ArrayList<Map.Entry<Integer, Object>>();
        this.offlineOrderFlag = false;
        this.terSoftInfoFlag = false;
        this.cacheData = new CacheData(this.sn);
    }

    public CacheData getDelayUpdateCacheData() {
        CacheData temp = new CacheData(this.sn);
        CacheData ret = null;
        ret = cacheData;
        cacheData = temp;
        return ret;
    }

    /**
     * 新连接接入，需要清除一些内存缓存数据，避免出现分布式部署情况下，终端频繁切换网关，导致内存数据老旧
     */
    public void reConnEvent() {
        // 清除位置缓存
        this.lastPos = null;
        // 清除电量缓存
        this.lastBattery = null;
        // 清除轨迹过滤计算环境
        fr.clearEnvir();
    }

    public TerPos getLastPos() {
        if (null == lastPos) {
            lastPos = TerGatewayData.getTerLastPosByMno(this.sn);
        }
        return lastPos;
    }

    public void updateLastPos(TerPos npos) {
        if (null == lastPos) {
            TerPos temp = new TerPos();
            temp.cloneObj(npos);
            lastPos = temp;
        } else {
            lastPos.cloneObj(npos);
        }
        cacheData.setLastPos(lastPos);
    }

    public TerBattery getLastBattery() {
        if (null == lastBattery) {
            lastBattery = TerGatewayData.getTerBatteryByMno(this.sn);
            if (null == lastBattery) {
                lastBattery = TerBattery.getDefTerbattery(this.sn);
            }
        }
        return lastBattery;
    }

    public TerBattery getCloneLastBattery() {
        if (null == lastBattery) {
            lastBattery = TerGatewayData.getTerBatteryByMno(this.sn);
            if (null == lastBattery) {
                lastBattery = TerBattery.getDefTerbattery(this.sn);
            }
        }
        TerBattery battery = new TerBattery();
        battery.clone(lastBattery);
        return battery;
    }

    public void updateTerInfo(TerInfo terInfo) {
        cacheData.setTerInfo(terInfo);
    }

    public void updateLastBattery(TerBattery battery) {
        if (null == lastBattery) {
            TerBattery temp = new TerBattery();
            temp.clone(battery);
            lastBattery = temp;
        } else {
            lastBattery.clone(battery);
        }
        cacheData.setLastBattery(lastBattery);
    }

    /**
     * 获取一个有效的流水号
     *
     * @return
     */
    public int getValidSerNo(Object obj) throws Exception {
        int serNo = 0;
        try {
            _lock.lock();
            if (_serList.isEmpty()) {
                serNo = 1;
            } else {
                Map.Entry<Integer, Object> entry = _serList.get(_serList.size() - 1);
                int existSerNo = entry.getKey();
                int iTestCnt = 0;
                boolean bOk = false;
                while (iTestCnt++ < MaxSerNo) {
                    serNo = (existSerNo + iTestCnt) % 256;
                    if (serNo == DefaultSerNo) {
                        continue;
                    } else if (containsSerNo(serNo)) {
                        continue;
                    } else {
                        bOk = true;
                        break;
                    }
                }
                if (!bOk) {
                    throw new Exception(String.format("为[%s]分配流水号异常", this.sn));
                }
            }
            _serList.add(new AbstractMap.SimpleEntry(serNo, obj));
            return serNo;
        } finally {
            _lock.unlock();
        }
    }

    public boolean containsSerNo(int serNo) {
        try {
            _lock.lock();
            int len = _serList.size();
            for (int i = 0; i < len; i++) {
                if (_serList.get(i).getKey() == serNo) {
                    return true;
                }
            }
            return false;
        } finally {
            _lock.unlock();
        }
    }

    public Object getSerNoAndDel(int serNo) {
        Object obj = null;
        try {
            _lock.lock();
            int len = _serList.size();
            for (int i = 0; i < len; i++) {
                if (_serList.get(i).getKey() == serNo) {
                    obj = _serList.get(i).getValue();
                    _serList.remove(i);
                    break;
                }
            }
            return obj;
        } finally {
            _lock.unlock();
        }
    }

    public AConnInfo getConnInfo() {
        AConnInfo connInfo = null;
        if (!StringUtil.IsNullOrEmpty(connId)) {
            connInfo = TerGatewayData.GetConnect(connId);
        }
        return connInfo;
    }

    /**
     * 检查离线指令
     */
    public void checkOfflineOrder() {
        String redisKey = String.format("%s.%s", TerGatewayData.REIDS_ORDER_LIST_TER, sn);
        RedisService redis = TerGatewayMain.getRedis();
        List<String> jsonObjList = redis.LRange(redisKey, 0, -1);
        List<String> delList = new LinkedList<>();
        Long planDtBase = System.currentTimeMillis();
        for (String jsonObj : jsonObjList) {
            boolean bDel = false;// 避免指令卡克，非正常逻辑都删除离线指令
            try {
                TerOfflineOrder terOfflineOrder = JSON.parseObject(jsonObj, TerOfflineOrder.class);
                if (null != terOfflineOrder) {
                    AProtocol protocol = AAutoProtocol.getProtocol(protocolName);
                    AConnInfo connInfo = getConnInfo();
                    if (null != protocol && null != connInfo) {
                        RemoteControl remoteControl = terOfflineOrder.toRemoteControl();
                        // 再尝试构造下行消息
                        final ByteBuf byteBuf = protocol.builtRemoteControlPkg(connInfo, remoteControl);
                        // 最后发送下行数据
                        if (byteBuf != null && byteBuf.readableBytes() > 0) {
                            planDtBase += 1000;// 延迟1s发送
                            remoteControl.planDownDt = planDtBase;
                            TerGatewayData.delayOfflineOrder.addOfflineOrder(remoteControl, jsonObj);
                        } else {
                            bDel = true;//不能构造成合法的下行报文 需要删除
                        }
                    } else {
                        break;
                    }
                } else {
                    logger.info(String.format("%s 离线指令流程,丢掉非法TerOfflineOrder结构对象", this.sn));
                    bDel = true;
                }
            } catch (Exception e) {
                logger.error("checkOfflineOrder", e);
                bDel = true;
            }
            // 删除指令
            if (bDel) {
                delList.add(jsonObj);
            }
        }
        // 删除无用的指令
        for (String str : delList) {
            redis.LDelValue(redisKey, 0, str);
        }
    }
}
